using System;
using System.Collections.Generic;
using Myotragus.Data.Domain;
using Myotragus.Data.Metadata;
using Myotragus.Data.Util;
using NHibernate;

namespace Myotragus.Data.Criteria
{
	public class LocalizableWriteQueryBuilder<TEntity, TId> : LocalizableQueryBuilder<TEntity>,
		ILocalizableWriteQueryBuilder<TEntity, TId>
		where TEntity : IEntity<TId>
	{
		public LocalizableWriteQueryBuilder(ISessionFactoryAdapter sessionFactory,
			ILocalizableEntityDescriptor<TEntity> entityDescriptor,
			ILocalizableEntityProjector<TEntity> projector, ILocalizableAlias alias)
			: base(sessionFactory, entityDescriptor, projector, alias)
		{
		}

		public void Save(int lcId, IEnumerable<TEntity> entities)
		{
			var session = OpenSession();

			foreach (var obj in entities)
				SaveEntity(session, obj, lcId);

			session.Flush();
		}

		public void Delete(IEnumerable<TEntity> entities)
		{
			var session = OpenSession();

			foreach (var obj in entities)
				DeleteEntity(session, obj);

			session.Flush();
		}

		private void DeleteEntity(ISession session, TEntity entity)
		{
			var save = GetSaveEntity(session, entity.Id);

			using (var transaction = session.BeginTransaction())
			{
				session.Delete(save);
				transaction.Commit();
			}
		}

		private void SaveEntity(ISession session, TEntity obj, int lcId)
		{
			var ops = CreateAndInitializeSaveEntity(session, obj, lcId);

			using (var transaction = session.BeginTransaction())
			{
				session.Save(ops);
				transaction.Commit();

				obj.Id = ops.Id;
			}
		}

		private IEntity<TId> CreateAndInitializeSaveEntity(ISession session,
			TEntity obj, int lcId)
		{
			var ops = IsUpdate(obj.Id)
							? GetSaveEntity(session, obj.Id)
							: CreateNewSaveEntity();

			UpdateValues(obj, ops, lcId);

			return ops;
		}

		private void UpdateValues(TEntity obj, IEntity ops, int lcId)
		{
			ReflectionHelper.InitializeObject(ops, obj);

			foreach (var prop in EntityDescriptor.LocalizableProperties)
			{
				var arrEmpty = ReflectionHelper.EmptyObjects;
				var value = (string)EntityDescriptor.EntityProperties[prop].GetValue(obj, arrEmpty);
				var text = (ILocalizableText)EntityDescriptor
												 .SaveEntityProperties[prop].GetValue(ops, arrEmpty);
				text.Values[lcId] = value;
			}
		}

		private IEntity<TId> CreateNewSaveEntity()
		{
			var result = (IEntity<TId>)Activator.CreateInstance(
				EntityDescriptor.SaveEntityImplementation);

			foreach (var name in EntityDescriptor.LocalizableProperties)
			{
				var prop = EntityDescriptor.SaveEntityProperties[name];
				var text = Activator.CreateInstance(prop.PropertyType);
				prop.SetValue(result, text, ReflectionHelper.EmptyObjects);
			}

			return result;
		}

		private IEntity<TId> GetSaveEntity(ISession session, TId id)
		{
			return (IEntity<TId>)session.Get(EntityDescriptor.SaveEntityImplementation, id);
		}

		private static bool IsUpdate(TId id)
		{
			// TODO: Better update or save
			return !Equals(id, default(TId));
		}
	}
}